#include "OgreGraphics.h"

#include <exception>

#include "OGRE/OgreRenderSystem.h"
#include "OGRE/OgreRenderWindow.h"
#include "OGRE/Overlay/OgreOverlayManager.h"
#include "OGRE/Overlay/OgreOverlayContainer.h"
#include "OGRE/Overlay/OgreFontManager.h"
#include "OGRE/Overlay/OgreFont.h"
#include "OGRE/Overlay/OgreTextAreaOverlayElement.h"

OgreGraphics::OgreGraphics(void)
{
	m_init = false;
	m_root = nullptr;
	m_window = nullptr;
	m_sceneMgr = nullptr;
	m_camera = nullptr;

	overlaySystem = nullptr;
}

OgreGraphics::~OgreGraphics(void)
{
   if(m_sceneMgr && overlaySystem)
	   m_sceneMgr->removeRenderQueueListener(overlaySystem);

	if (m_camera)
		m_camera = nullptr;

	if (m_sceneMgr)
		m_sceneMgr = nullptr;

	if (m_window)
		m_window = nullptr;
	
	if (m_root)
	{
		delete m_root;
		m_root = nullptr;
	};
}

int OgreGraphics::init()
{
	if (m_init)
		return 0;

	try
	{
		Ogre::String lConfigFileName = "";
		Ogre::String lPluginsFileName = "";

		Ogre::String lLogFileName = "Ogre.log";

		// I create the root and I wrap it in an auto_ptr so that it will be automatically released.
		// Now I can even do "throw std::bad_alloc("bad alloc");", the program will release the root smoothly.
		m_root = new Ogre::Root(lConfigFileName, lPluginsFileName, lLogFileName);

		typedef std::vector<Ogre::String> Strings;

		Strings lPluginNames;
		lPluginNames.push_back("RenderSystem_GL");
		//lPluginNames.push_back("RenderSystem_Direct3D9");
		lPluginNames.push_back("Plugin_ParticleFX");
		lPluginNames.push_back("Plugin_CgProgramManager");
		//lPluginNames.push_back("Plugin_PCZSceneManager");
		//lPluginNames.push_back("Plugin_OctreeZone");
		lPluginNames.push_back("Plugin_OctreeSceneManager");
		//lPluginNames.push_back("Plugin_BSPSceneManager");

		for(Strings::iterator lIter = lPluginNames.begin(); lIter != lPluginNames.end(); lIter++)
		{
			Ogre::String& lPluginName = (*lIter);
			bool lIsInDebugMode = OGRE_DEBUG_MODE;
			if(lIsInDebugMode)
				lPluginName.append("_d");
			m_root->loadPlugin(lPluginName);
		};

		const Ogre::RenderSystemList& lRenderSystemList = m_root->getAvailableRenderers();
		if (lRenderSystemList.size() == 0 )
			throw ("Sorry, no rendersystem was found.");

		Ogre::RenderSystem *lRenderSystem = lRenderSystemList[0];
		m_root->setRenderSystem(lRenderSystem);

		// I can create a window automatically, but I won't do it.
		bool lCreateAWindowAutomatically = false;
		// name of the automatically generated window. empty for me.
		Ogre::String lWindowTitle = "";
		// custom capabilities of the rendersystem. It's a feature for advanced use.
		Ogre::String lCustomCapacities = "";
		m_root->initialise(lCreateAWindowAutomatically, lWindowTitle, lCustomCapacities);

		lWindowTitle = "start 007";
		unsigned int lSizeX = 800;
		unsigned int lSizeY = 600;
		//I don't want to use fullscreen during development.
		bool lFullscreen = false; 
		// This is just an example of parameters that we can put. Check the API for more details.
		Ogre::NameValuePairList lParams;
		// fullscreen antialiasing. (check wikipedia if needed).
		lParams["FSAA"] = "0"; 
		// vertical synchronisation will prevent some image-tearing, but also
		// will provide smooth framerate in windowed mode.(check wikipedia if needed).
		lParams["vsync"] = "true";
		m_window = m_root->createRenderWindow(lWindowTitle, lSizeX, lSizeY, lFullscreen, &lParams);
	} catch(Ogre::Exception &e)
	{
		throw e.what();
	} catch(std::exception &e)
	{
		throw e.what();
	}

	m_init = true;
	return 0;
}

bool OgreGraphics::isActiveWindow()
{
	if (m_init && m_window)
		return !m_window->isClosed();

	return false;
}

void OgreGraphics::draw()
{
	m_window->update(false);
 
	// The drawn surface is then shown on the screen
	// (google "double buffering" if you want more details).
	// I always use vertical synchro.
	bool lVerticalSynchro = true;
	m_window->swapBuffers(lVerticalSynchro);
 
	// This update some internal counters and listeners.
	// Each render surface (window/rtt/mrt) that is 'auto-updated' has got its 'update' function called.
	m_root->renderOneFrame();

	Ogre::Degree lAngle(1);

	fuckingYup->yaw(lAngle);

	lAngle = 0.1f;

	fuckingSun += lAngle;
	if (fuckingSun.valueDegrees() > 360.0f)
		fuckingSun = fuckingSun.valueDegrees() - 360.0f;

	Ogre::Real x;
	Ogre::Real z;
	Ogre::Vector3 vctPlan = fuckingYup->getPosition();
	calcFuckingCircle(&x, &z, vctPlan.x, vctPlan.z, 5000.0f, fuckingSun);

	lFuckingSun->setPosition(x, 0.0f, z);
//	lFuckingSun->setDirection(0, -1.0f, 1);

	Ogre::Vector3 vct = m_camera->getPosition();
	calcFuckingCircle(&x, &z, vct.x, vct.z, 4900.0f, fuckingSun);
	fuckingYup2->setPosition(x, 0.0f, z);
	fuckingYup2->yaw(-lAngle);

	Ogre::WindowEventUtilities::messagePump();
}

void OgreGraphics::calcFuckingCircle(Ogre::Real *x, Ogre::Real *y, Ogre::Real x0, Ogre::Real y0, Ogre::Real R, Ogre::Degree angle)
{
	Ogre::Real xc = x0 + R * Ogre::Math::Cos(angle);
	Ogre::Real yc = y0 + R * Ogre::Math::Sin(angle);

	*x = xc;
	*y = yc;
}

void OgreGraphics::fillscene()
{
	fuckingSun = 0;

	m_sceneMgr = m_root->createSceneManager(Ogre::ST_GENERIC, "MyFirstSceneManager");

	Ogre::SceneNode* lRootSceneNode = m_sceneMgr->getRootSceneNode();
 
	// I create a camera. It represent a 'point of view' in the scene.
	Ogre::Camera* lCamera = m_sceneMgr->createCamera("MyFirstCamera");
 
	// I attach the camera to a new SceneNode. It will be easier then to move it in the scene.
	Ogre::SceneNode* lCameraNode = lRootSceneNode->createChildSceneNode("MyFirstCameraNode");
	lCameraNode->attachObject(lCamera);
 
	float lViewportWidth = 1.0f;
	float lViewportHeight = 1.0f;
	float lViewportLeft	= 0.0f;
	float lViewportTop = 0.0f;
	unsigned short lMainViewportZOrder = 100;
	Ogre::Viewport * vp = m_window->addViewport(lCamera, lMainViewportZOrder, lViewportLeft, lViewportTop, lViewportWidth, lViewportHeight);
 
	// I want the viewport to draw the scene automatically
	// when I will call lWindow->update();
	vp->setAutoUpdated(true);
 
	// I choose a color for this viewport. 
	// I prefer to have a bright color, to detect holes in geometry etc...
	vp->setBackgroundColour(Ogre::ColourValue(1,0,1));
 
	// I choose the visual ratio of the camera. To make it looks real, I want it the same as the viewport.
	float ratio = float(vp->getActualWidth()) / float(vp->getActualHeight());
	lCamera->setAspectRatio(ratio);
 
	// I choose the clipping far& near planes. if far/near>2000, you can get z buffer problem.
	// eg : far/near = 10000/5 = 2000 . it's ok.
	// If (far/near)>2000 then you will likely get 'z fighting' issues.
	lCamera->setNearClipDistance(1.5f);
	lCamera->setFarClipDistance(5000.0f); 
	m_camera = lCamera;
	// I want my window to be active
	m_window->setActive(true);
 
	// I want to update myself the content of the window, not automatically.
	m_window->setAutoUpdated(false);

	// I get a reference on the material manager, which is a singleton.
	Ogre::MaterialManager& lMaterialManager = Ogre::MaterialManager::getSingleton();

	Ogre::String lNameOfResourceGroup = "Mission 1 : Deliver Tom";

	Ogre::ResourceGroupManager& lRgMgr = Ogre::ResourceGroupManager::getSingleton();
	lRgMgr.createResourceGroup(lNameOfResourceGroup);
 
	Ogre::String lDirectoryToLoadTextures = "c:/Work/Projects/projhimskforest/start007/start007/media";
	bool lIsRecursive = false;
	lRgMgr.addResourceLocation(lDirectoryToLoadTextures, "FileSystem", lNameOfResourceGroup, lIsRecursive);

	try
	{
 		lRgMgr.initialiseResourceGroup(lNameOfResourceGroup);
		lRgMgr.loadResourceGroup(lNameOfResourceGroup);
	} catch(Ogre::Exception &e)
	{
		throw e.what();
	}

	Ogre::SceneNode* lLightSceneNode = NULL;
	{
		Ogre::Light* lLight = m_sceneMgr->createLight();
 
		lLight->setType(Ogre::Light::LT_POINT);
		//lLight->setType(Ogre::Light::LT_DIRECTIONAL);
 
		lLight->setDiffuseColour(1.0f, 1.0f, 1.0f);
		lLight->setSpecularColour(1.0f, 1.0f, 1.0f);// color of 'reflected' light
 
		lLightSceneNode = lRootSceneNode->createChildSceneNode();
		lLightSceneNode->attachObject(lLight);
	}

	Ogre::ManualObject* lManualObject = NULL;

	// The manualObject creation requires a name.
	Ogre::String lManualObjectName = "CubeWithAxes";
	lManualObject = m_sceneMgr->createManualObject(lManualObjectName);
 
	// Always tell if you want to update the 3D (vertex/index) later or not.
	bool lDoIWantToUpdateItLater = false;
	lManualObject->setDynamic(lDoIWantToUpdateItLater);
 
	// Here I create a cube in a first part with triangles, and then axes (in red/green/blue).
 	// BaseWhiteNoLighting is the name of a material that already exist inside Ogre.
	// Ogre::RenderOperation::OT_TRIANGLE_LIST is a kind of primitive.
	float lSize = 0.7f;

	lManualObject->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST);
	float cp = 1.0f * lSize ;
	float cm = -1.0f * lSize;
	float lDiffuseScale = 1.0f;
	float lLightmapScale = 1.0f;
 
	lManualObject->position(cm, cp, 0.0f);// a vertex
	lManualObject->colour(Ogre::ColourValue(0.0f,1.0f,0.0f,1.0f));
	lManualObject->normal(0.0, 0.0, 1.0f);
	lManualObject->textureCoord(0.0f, 0.0f);
	lManualObject->textureCoord(0.0f, 0.0f);
 
	lManualObject->position(cp, cp, 0.0f);// a vertex
	lManualObject->colour(Ogre::ColourValue(1.0f,1.0f,0.0f,1.0f));
	lManualObject->normal(0.0, 0.0, 1.0f);
	lManualObject->textureCoord(lDiffuseScale, 0.0f);
	lManualObject->textureCoord(lLightmapScale, 0.0f);
 
	lManualObject->position(cp, cm, 0.0f);// a vertex
	lManualObject->colour(Ogre::ColourValue(1.0f,0.0f,0.0f,1.0f));
	lManualObject->normal(0.0, 0.0, 1.0f);
	lManualObject->textureCoord(lDiffuseScale, lDiffuseScale);
	lManualObject->textureCoord(lLightmapScale, lLightmapScale);
 
	lManualObject->position(cm, cm, 0.0f);// a vertex
	lManualObject->colour(Ogre::ColourValue(0.0f,0.0f,0.0f,1.0f));
	lManualObject->normal(0.0, 0.0, 1.0f);
	lManualObject->textureCoord(0.0, lDiffuseScale);
	lManualObject->textureCoord(0.0, lLightmapScale);
 
	lManualObject->triangle(2,1,0);
	lManualObject->triangle(0,3,2);	

	lManualObject->end();

	Ogre::String lNameOfTheMesh = "MeshCubeAndAxe";
	Ogre::String lResourceGroup = Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME;
	lManualObject->convertToMesh(lNameOfTheMesh);
 
	lNameOfTheMesh = "GeoSphere.mesh";
	Ogre::Entity* lEntity = m_sceneMgr->createEntity(lNameOfTheMesh);
	Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode();
	lNode->attachObject(lEntity);
	lNode->translate(0.0f, 0.0f, 0.0f);

	Ogre::Degree lAngle(26.0);
	lNode->roll(lAngle);
	fuckingYup = lNode;

	//lLightSceneNode->translate(40.0f, 0.0f, 0.0f);
	lCameraNode->translate(0.0f, 0.0f, 20.0f);

	lFuckingSun = lLightSceneNode;
	
	lEntity = m_sceneMgr->createEntity("MeshCubeAndAxe");
	lEntity->setMaterialName("SunStarMaterial");
	lNode = lRootSceneNode->createChildSceneNode();
	lNode->attachObject(lEntity);
	lNode->translate(15.0f, 0.0f, 5.0f);
	lNode->yaw(Ogre::Degree(-90));
	fuckingYup2 = lNode;
	fuckingYup2->scale(60.0f, 60.0f, 60.0f);


	try
	{
		m_sceneMgr->setSkyBox(true, "Examples/SpaceSkyBox", 2500);
	} catch(Ogre::Exception &e)
	{
		throw e.what();
	}

	overlaySystem = new Ogre::OverlaySystem();
	m_sceneMgr->addRenderQueueListener(overlaySystem);

	Ogre::OverlayManager *pOverlayMgr = Ogre::OverlayManager::getSingletonPtr();
	Ogre::Overlay *pOverlay = pOverlayMgr->create( "overlay" );

	pOverlay->setZOrder(500);

	Ogre::OverlayContainer* pPanel = static_cast<Ogre::OverlayContainer*>(pOverlayMgr->createOverlayElement("Panel", "container"));
	pPanel->setPosition( 0, 0 );
	pPanel->setDimensions(0.4, 0.4);
	pPanel->setMaterialName("SunLIghtMaterial");
	pOverlay->add2D( pPanel );


	// Create a text area
	Ogre::TextAreaOverlayElement* textArea = static_cast<Ogre::TextAreaOverlayElement*>(pOverlayMgr->createOverlayElement("TextArea", "TextAreaName"));
	textArea->setMetricsMode(Ogre::GMM_PIXELS);
	textArea->setPosition(300, 300);
	textArea->setDimensions(100, 100);
	textArea->setCaption("Hello, World!");
	textArea->setCharHeight(16);
	try
	{
		Ogre::FontPtr pFont = Ogre::FontManager::getSingletonPtr()->create("MyFont", "Mission 1 : Deliver Tom");
		pFont->setType( Ogre::FT_TRUETYPE );
		pFont->setSource( "cuckoo.ttf" );
		pFont->setTrueTypeSize(16);
		pFont->load();

//		Ogre::ResourcePtr dsf = Ogre::FontManager::getSingleton().getByName("MyFont");
//		Ogre::FontManager::getSingleton().getByName("MyFont")->load();
		textArea->setFontName("MyFont");
	} catch(Ogre::Exception &e)
	{
		throw e.what();
	}


	textArea->setColourBottom(Ogre::ColourValue(0.3, 0.5, 0.3));
	textArea->setColourTop(Ogre::ColourValue(0.5, 0.7, 0.5));
 
	// Add the text area to the panel
	pPanel->addChild(textArea);

	pOverlay->show();

	m_root->clearEventTimes();
}

std::string OgreGraphics::getHWnd()
{
   unsigned int hWnd = 0;
   m_window->getCustomAttribute("WINDOW", &hWnd);

   return Ogre::StringConverter::toString(hWnd);
}

void OgreGraphics::fuckingCameraUp()
{
	m_camera->pitch(Ogre::Degree(2));
}
void OgreGraphics::fuckingCameraDown()
{
	m_camera->pitch(Ogre::Degree(-2));
}
void OgreGraphics::fuckingCameraRight()
{
	m_camera->yaw(Ogre::Degree(-2));
}
void OgreGraphics::fuckingCameraLeft()
{
	m_camera->yaw(Ogre::Degree(2));
}
void OgreGraphics::fuckingCameraTranslate(float dx, float dy, float dz)
{
	Ogre::Vector3 vct = m_camera->getPosition();
	vct.x += dx;
	vct.y += dy;
	vct.z += dz;
	m_camera->setPosition(vct);
}

Ogre::RenderWindow* OgreGraphics::getWindow()
{
	return m_window;
}
Ogre::Root *OgreGraphics::getRoot()
{
	return m_root;
}